<template>
  <div ref="canvas"></div>
</template>

<script lang="ts" setup>
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import Stats from "three/examples/jsm/libs/stats.module";
import { GUI } from "three/examples/jsm/libs/lil-gui.module.min.js";
import { ref, onMounted } from "vue";

// 画布
const canvas = ref<any>('');
// 场景
const scene = new THREE.Scene();
// 相机
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(-30, 40, 30);
camera.lookAt(scene.position);
// 渲染器
const renderer = new THREE.WebGLRenderer();
renderer.setClearColor(0x000000);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
// 相机控件
const orbitControl = new OrbitControls(camera, renderer.domElement);
// 平面
const planeGeometry = new THREE.PlaneGeometry(60, 40, 1, 1);
const planeMaterial = new THREE.MeshLambertMaterial({
  color: 0xffffff,
});
const plane = new THREE.Mesh(planeGeometry, planeMaterial);
plane.receiveShadow = true;
// 在数学上，物体旋转一周为 2π 那么这里就是沿X轴向左旋转(逆时针) 90 度
// 因为默认的矩形平面是在XOY平面上，如果想要让矩形平面在XOZ平面上，就需要手动旋转
plane.rotation.x = -0.5 * Math.PI;
plane.position.set(0, 0, 0);
scene.add(plane);
// 立方体
const boxGeometry = new THREE.BoxGeometry(5, 8, 3);
const boxMaterial = new THREE.MeshLambertMaterial({ color: 0x44ff44 });
const box = new THREE.Mesh(boxGeometry, boxMaterial);
box.position.y = 4;
box.castShadow = true;
scene.add(box);
// 光源
const ambient = new THREE.AmbientLight(0x3c3c3c);
scene.add(ambient);
const spot = new THREE.SpotLight(0xffffff, 1, 180, Math.PI / 4);
spot.position.set(-40, 30, 20);
spot.castShadow = true;
spot.shadow.mapSize.width = window.innerWidth;
spot.shadow.mapSize.height = window.innerHeight;
scene.add(spot);
// 性能统计
const stat = Stats();
stat.setMode(0);
// GUI界面
const gui = new GUI();
// GUI控制项
const controlObj = {
  // 是否展示
  visible: true,
  // 缩放
  scaleX: 1,
  scaleY: 1,
  scaleZ: 1,
  // 坐标点
  positionX: 0,
  positionY: 4,
  positionZ: 0,
  // 旋转
  rotationX: 0,
  rotationY: 0,
  rotationZ: 0,
  // 平移
  translateX: 0,
  translateY: 0,
  translateZ: 0,
  translate: () => {
    // 设置的不是物体的绝对位置，而是物体相对于当前位置的平移距离
    // 比如原来在 (1, 2, 3) 调用 translateX(4) 之后，就会移动到 (5, 2, 3) 的位置上
    box.translateX(controlObj.translateX);
    box.translateY(controlObj.translateY);
    box.translateZ(controlObj.translateZ);

    controlObj.positionX = box.position.x;
    controlObj.positionY = box.position.y;
    controlObj.positionZ = box.position.z;
  },
};
// 是否展示
// @ts-ignore
gui.add(controlObj, 'visible').name("是否展示");
// 缩放
const scale = gui.addFolder("缩放");
// @ts-ignore
scale.add(controlObj, 'scaleX', -4, 4).name("X轴");
// @ts-ignore
scale.add(controlObj, 'scaleY', -4, 4).name("Y轴");
// @ts-ignore
scale.add(controlObj, 'scaleZ', -4, 4).name("Z轴");
// 坐标位置
const position = gui.addFolder("坐标位置");
// @ts-ignore
position.add(controlObj, 'positionX',  -10, 10).name("X轴").listen().onChange((val) => {
  box.position.x = controlObj.positionX;
});
// @ts-ignore
position.add(controlObj, 'positionY', -4, 20).name("Y轴").listen().onChange((val) => {
  box.position.y = controlObj.positionY;
});
// @ts-ignore
position.add(controlObj, 'positionZ',  -10, 10).name("Z轴").listen().onChange((val) => {
  box.position.z = controlObj.positionZ;
});
// 旋转
const rotate = gui.addFolder("旋转");
// @ts-ignore
rotate.add(controlObj, "rotationX", -4, 4).name("X轴");
// @ts-ignore
rotate.add(controlObj, "rotationY", -4, 4).name("Y轴");
// @ts-ignore
rotate.add(controlObj, "rotationZ", -4, 4).name("Z轴");
// 平移
const translate = gui.addFolder("平移");
// @ts-ignore
translate.add(controlObj, 'translateX', -10, 10).name("X轴");
// @ts-ignore
translate.add(controlObj, 'translateY', -10, 10).name("Y轴");
// @ts-ignore
translate.add(controlObj, 'translateZ', -10, 10).name("Z轴");
// @ts-ignore
translate.add(controlObj, 'translate').name("平移");

// 渲染函数
const render = () => {
  stat.update();
  orbitControl.update();

  box.visible = controlObj.visible;
  box.rotation.x = controlObj.rotationX;
  box.rotation.y = controlObj.rotationY;
  box.rotation.z = controlObj.rotationZ;

  box.scale.set(controlObj.scaleX, controlObj.scaleY, controlObj.scaleZ);

  requestAnimationFrame(render);
  renderer.render(scene, camera);
};

onMounted(() => {
  canvas.value.appendChild(renderer.domElement);
  canvas.value.appendChild(stat.domElement);
  render();
});
</script>
